package com.agil.photoalbum.db.dao;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.TreeMap;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.Address;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.Multipart;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;

import org.hibernate.Query;
import org.hibernate.Session;

import com.agil.photoalbum.db.entities.Category;
import com.agil.photoalbum.db.entities.Comment;
import com.agil.photoalbum.db.entities.Picture;
import com.agil.photoalbum.db.entities.User;
import com.agil.photoalbum.db.util.SessionManager;
import com.agil.photoalbum.util.Thumbnail;

public class Functions {

	public static final String SMTP_SERVER = "smtp.bol.bg";
	private static final int THUMBNAIL_MAX_WIDTH = 100;
	private static final int THUMBNAIL_MAX_HEIGHT = 100;

	public static Session session = null;

	// ##############################################################################
	// pictures methods
	public static List<Picture> searchPictureByName(String name) {

		List<Picture> queryResult = null;
		try {
			session = SessionManager.openSession();
			Query query = session
					.createQuery("from Picture p where p.pictureName like '%"
							+ name + "%'");
			queryResult = query.list();
		} finally {
			if (session != null) {
				SessionManager.closeSession();
			}
		}
		return queryResult;
	}
	
//	public static List<Picture> searchPictureForUserByName(String name, User user)
//	{
//		List<Picture> queryResult = null;
//		try {
//			session = SessionManager.openSession();
//			Query query = session
//					.createQuery("from Picture p join  where p.pictureName like '%"
//							+ name + "%'");
//			queryResult = query.list();
//		} finally {
//			if (session != null) {
//				SessionManager.closeSession();
//			}
//		}
//		return queryResult;
//	}

	public static Picture getPicture(String pic_id) {

		List<Picture> queryResult = null;
		try {
			session = SessionManager.openSession();
			Query query = session
					.createQuery("from Picture p where p.pictureId='" + pic_id
							+ "'");
			queryResult = query.list();
			if (!queryResult.isEmpty())
				return queryResult.get(0);
		} finally {
			if (session != null) {
				SessionManager.closeSession();
			}
		}
		return null;
	}

	// TODO query improvement
	public static List<Picture> searchPictureByNameForUser(User user,
			String name) {

		List<Picture> queryResult = null;
		try {
			session = SessionManager.openSession();
			Query query = session
					.createQuery("from Picture p left join fetch p.categories c left join fetch c.user u where u.userId='"
							+ user.getUserId() + "'");
			queryResult = query.list();
		} finally {
			if (session != null) {
				SessionManager.closeSession();
			}
		}

		return queryResult;
	}

	public static Picture getPictureByPath(String picPath) {
		List<Picture> queryResult = null;
		try {
			session = SessionManager.openSession();
			Query query = session
					.createQuery("from Picture p where p.picturePath='"
							+ picPath + "'");
			queryResult = query.list();
			if (!queryResult.isEmpty())
				return queryResult.get(0);
		} finally {
			if (session != null) {
				SessionManager.closeSession();
			}
		}
		return null;
	}

	public static boolean addPicture(Byte[] stream, String catID, String picName) {
		boolean status = false;
		try {
			session = SessionManager.openSession();
			if (validatePicture(stream)) {
				Picture p = new Picture();
				p.setPictureId(Long.parseLong(catID));
				p.setPictureName(picName);
				HibernateBaseDAO hbDAO = new HibernateBaseDAO();
				status = hbDAO.save(p);
			}
		} finally {
			if (session != null) {
				SessionManager.closeSession();
			}
		}
		return status;
	}

	public static boolean validatePicture(Byte[] stream) {

		int fileHeader = stream[0] | stream[1] << 8;
		switch (fileHeader) {
		case 19778: // .bmp
			return true;
		case 55551: // .jpeg
			return true;
		case 20617: // .png
			return true;
		case 18759: // .gif
			return true;
		default:
			return false;
		}
	}

	public static boolean deletePicture(String picID) {
		boolean status = false;
		try {
			session = SessionManager.openSession();
			HibernateBaseDAO hbDAO = new HibernateBaseDAO();
			Picture p = hbDAO.findById(Picture.class, Long.parseLong(picID));
			status = hbDAO.delete(p);

		} finally {
			if (session != null) {
				SessionManager.closeSession();
			}
		}
		return status;
	}

	public static boolean renamePicture(String picID, String picName) {
		boolean status = false;
		try {
			session = SessionManager.openSession();
			HibernateBaseDAO hbDAO = new HibernateBaseDAO();
			Picture p = hbDAO.findById(Picture.class, Long.parseLong(picID));
			p.setPictureName(picName);
			status = hbDAO.save(p);
		} finally {
			if (session != null) {
				SessionManager.closeSession();
			}
		}
		return status;
	}

	public static List<Picture> getAllPicturesByDate() {
		List<Picture> queryResult = null;
		try {
			session = SessionManager.openSession();
			Query query = session.createQuery("from Picture p");

			queryResult = (List<Picture>) query.list();

			StringBuilder sb = new StringBuilder();
			for (Picture picture : queryResult) {

				String picPath = picture.getPicturePath();
				int indexOfLastDot = picPath.lastIndexOf(".");
				sb.append(picPath.substring(0, indexOfLastDot));
				sb.append("_tbm");
				sb.append(picPath.substring(indexOfLastDot));

				picture.setPicturePath(sb.toString());
				sb.delete(0, sb.length());
			}
		} finally {
			if (session != null) {
				SessionManager.closeSession();
			}
		}

		return queryResult;
	}

	public static List<Picture> getAllPicturesByDateForUser(String userID) {
		List<Picture> queryResult = null;
		try {
			session = SessionManager.openSession();
			Query query = session
					.createQuery("from Picture p left join fetch p.categories c left join fetch c.user u where u.userId='"
							+ userID + "' order by p.pictureName");

			queryResult = query.list();

		} finally {
			if (session != null) {
				SessionManager.closeSession();
			}
		}
		return queryResult;
	}

	public static List<Picture> getPicturesForCategory(Category cat) {
		List<Picture> queryResult = null;
		try {
			if (cat != null) {
				session = SessionManager.openSession();
				Query query = session
						.createQuery("from Picture p join fetch p.categories as c where c.categoryId="
								+ cat.getCategoryId());

				queryResult = query.list();
			}
		} finally {
			if (session != null) {
				SessionManager.closeSession();
			}
		}
		return queryResult;
	}

	public static List<Picture> getPicturesForCategoryByCatId(String catId) {
		List<Picture> queryResult = null;
		Category c = null;
		try {
			session = SessionManager.openSession();
			HibernateBaseDAO hbDAO = new HibernateBaseDAO();
			c = hbDAO.findById(Category.class, Long.valueOf(catId));

		} finally {
			if (session != null) {
				SessionManager.closeSession();
			}
		}
		return getPicturesForCategory(c);
	}

	// ##############################################################################
	// categories methods
	public static Category getRootCategory(String cat_id) {
		List<Category> queryResult = null;
		Category c = null;
		try {
			session = SessionManager.openSession();
			HibernateBaseDAO hbDAO = new HibernateBaseDAO();
			c = hbDAO.findById(Category.class, Long.parseLong(cat_id));
			if (c != null) {

				while (c.getCategory() != null
						& c.getCategoryId() != c.getCategory().getCategoryId())
					c = c.getCategory();
			}
		} finally {
			if (session != null) {
				SessionManager.closeSession();
			}
		}
		return c;
	}

	public static Category getRootCategoryForUser(String userId) {
		List<Picture> queryResult = null;
		Category cat = null;
		try {
			session = SessionManager.openSession();
			Query query = session
					.createQuery("from Category c where c.user.userId='"
							+ userId + "' and c.category=null");

			cat = (Category) query.uniqueResult();
		} finally {
			if (session != null) {
				SessionManager.closeSession();
			}
		}
		return cat;
	}

	public static TreeMap<Long, Category> getAllCategoriesForUser(String userId) {
		TreeMap catsMap = null;
		Category cat = null;
		try {
			session = SessionManager.openSession();
			cat = getRootCategoryForUser(userId);
			if (cat != null) {
				catsMap = new TreeMap<Integer, Category>();

				catsMap.put(0l, cat);

				putCatsForCatInTreeMap(catsMap, cat);
			}
		} finally {
			if (session != null) {
				SessionManager.closeSession();
			}
		}
		return catsMap;
	}

	private static void putCatsForCatInTreeMap(TreeMap<Long, Category> map,
			Category cat) {
		List<Category> cats = getCategoriesForCategory(cat);
		if (cats != null) {
			for (Category c : cats) {
				map.put(cat.getCategoryId(), c);
				if (getCategoriesForCategory(c).size() > 0) {
					putCatsForCatInTreeMap(map, c);
				}
			}
		}
	}

	public static List<Category> getCategoriesForCategory(Category cat) {
		List<Category> cats = null;
		try {
			session = SessionManager.openSession();
		Query query = session
				.createQuery("from Category c where c.category.categoryId='"
						+ cat.getCategoryId() + "'");

		cats = query.list();
		}finally{
			if (session != null) {
				SessionManager.closeSession();
			}
		}
		return cats;
	}

	public static boolean createCategory(String user_id, String parentCat_id, String catName) {
		boolean status = false;
		try {
			session = SessionManager.openSession();
			HibernateBaseDAO hbDAO = new HibernateBaseDAO();
			Category parentCategory = hbDAO.findById(Category.class, Long
				.getLong(parentCat_id));
			User user = hbDAO.findById(User.class, Long.getLong(user_id));

			Category cat = new Category();
			cat.setCategory(parentCategory);
			cat.setUser(user);
			cat.setCategoryName(catName);
			status = hbDAO.save(cat);
		} finally {
			if (session != null) {
				SessionManager.closeSession();
			}
		}
		return status;
	}

	public static boolean deleteCategory(String catID) {
		boolean status = false;
		try {
			session = SessionManager.openSession();
			HibernateBaseDAO hbDAO = new HibernateBaseDAO();
			Category c = hbDAO.findById(Category.class, Long.parseLong(catID));
			if (c != null) {
				status = hbDAO.delete(c);
			}
		} finally {
			if (session != null) {
				SessionManager.closeSession();
			}
		}
		return status;
	}

	public static boolean renameCategory(String catID, String newName) {
		boolean status = false;
		try {
			session = SessionManager.openSession();
			HibernateBaseDAO hbDAO = new HibernateBaseDAO();
			Category c = hbDAO.findById(Category.class, Long.parseLong(catID));
			if (c != null) {
				c.setCategoryName(newName);
				status = hbDAO.save(c);
			}
		} finally {
			if (session != null) {
				SessionManager.closeSession();
			}
		}
		return status;
	}

	// ##############################################################################
	// comments methods
	public static List<Comment> getCommentsForPicture(String picID) {
		List<Comment> queryResult = null;
		try {
			session = SessionManager.openSession();
		Query query = session
				.createQuery("from Comment c where c.picture.pictureId='"
						+ picID + "' order by c.commentTimestamp");
		queryResult = query.list();
		}finally{
			if (session != null) {
				SessionManager.closeSession();
			}
		}
		return queryResult;
	}

	public static boolean addComment(String user_id, String text, String pic_id) {
		boolean status = false;
		try {
			session = SessionManager.openSession();
			HibernateBaseDAO hbDAO = new HibernateBaseDAO();
			User user = hbDAO.findById(User.class, Long.parseLong(user_id));
			Picture pic = hbDAO.findById(Picture.class, Long.parseLong(pic_id));
			if (user != null && pic != null) {
				Comment comment = new Comment();
				comment.setUser(user);
				comment.setPicture(pic);
				comment.setCommentMsg(text);

				status = hbDAO.save(comment);
			}
		} finally {
			if (session != null) {
				SessionManager.closeSession();
			}
		}
		return status;
	}

	public static boolean deleteComment(String commentID) {
		boolean status = false;
		try {
			session = SessionManager.openSession();
			HibernateBaseDAO hbDAO = new HibernateBaseDAO();
			Comment c = hbDAO
					.findById(Comment.class, Long.parseLong(commentID));
			status = hbDAO.delete(c);
		} finally {
			if (session != null) {
				SessionManager.closeSession();
			}
		}
		return status;

	}

	// ##############################################################################
	// users methods
	public static User getUserById(String userId) {
		User u = null;
		try {
			session = SessionManager.openSession();
			HibernateBaseDAO hbDAO = new HibernateBaseDAO();
			u = hbDAO.findById(User.class, new Long(userId));
		} finally {
			if (session != null) {
				SessionManager.closeSession();
			}
		}
		return u;
	}

	public static User getUserByUserName(String userName) {
		User user = null;
		try {
			session = SessionManager.openSession();
			Query query = session.createQuery("from User u where u.userName='"
					+ userName + "'");
			user = (User) query.uniqueResult();

		} finally {
			if (session != null) {
				SessionManager.closeSession();
			}
		}
		return user;
	}

	public static List<User> getAllUsers() {
		List<User> queryResult = null;
		try {
			session = SessionManager.openSession();
			HibernateBaseDAO hbDAO = new HibernateBaseDAO();
			queryResult = hbDAO.findAll(User.class);
		} finally {
			if (session != null) {
				SessionManager.closeSession();
			}
		}
		return queryResult;
	}

	public static boolean addUser(User user) {

		// check first if the user is unique
		boolean status = false;
		try {
			session = SessionManager.openSession();
			HibernateBaseDAO hbDAO = new HibernateBaseDAO();
			User u = new User(user);
			status = hbDAO.save(u);
		} finally {
			if (session != null) {
				SessionManager.closeSession();
			}
		}

		return status;

		// to add a directory named "root" automatically for each user
	}

	public static boolean deleteUser(String userID) {
		boolean status = false;
		try {
			session = SessionManager.openSession();
		HibernateBaseDAO hbDAO = new HibernateBaseDAO();
		User u = hbDAO.findById(User.class, Long.getLong(userID));
		status = hbDAO.delete(u);
		} finally {
			if (session != null) {
				SessionManager.closeSession();
			}
		}
		return status;
	}

	public static boolean editUser(User user) {
		boolean status = false;
		try {
			session = SessionManager.openSession();
			HibernateBaseDAO hbDAO = new HibernateBaseDAO();
			User u = hbDAO.findById(User.class, user.getUserId());

			u.setUserName(user.getUserName());
			u.setUserPass(user.getUserPass());
			u.setFName(user.getFName());
			u.setLName(user.getLName());
			u.setCategories(user.getCategories());
			u.setComments(user.getComments());
			u.setRoles(user.getRoles());
			status = hbDAO.save(u);
		} finally {
			if (session != null) {
				SessionManager.closeSession();
			}
		}
		return status;
	}

//	public static String getUserMail(String userId) {
//		Session session = SessionManager.openSession();
//		Query query = session
//				.createQuery("select u.email from User u where u.userId="
//						+ userId);
//		try {
//			List result = query.list();
//			if (result.isEmpty())
//				return null;
//			else
//				return (String) result.get(0);
//		} finally {
//			session.close();
//		}
//	}

	public static void addDirToUser(File dir, int userId, int pCategoryId) {
		if (dir.isFile()) {
			Byte[] fileStream = getFileAsByteArray(dir);
			if (validatePicture(fileStream)) {
				addPicture(fileStream, "" + pCategoryId, dir.getName());
			}
		} else if (dir.isDirectory()) {
			File[] dirContent = dir.listFiles();
			if (dirContent.length == 0)
				createCategory("" + userId, "" + pCategoryId, dir.getName());
			else
				for (File f : dirContent)
					addDirToUser(f, userId, pCategoryId);

		}
	}

	public static Byte[] getFileAsByteArray(File file) {
		try {
			InputStream inputFile = new FileInputStream(file);
			long fileLength = file.length();

			if (fileLength > Integer.MAX_VALUE)
				return null;

			byte[] bytes = new byte[(int) fileLength];
			int offset = 0;
			int numRead = 0;
			while (offset < bytes.length
					&& (numRead = inputFile.read(bytes, offset, bytes.length
							- offset)) >= 0) {
				offset += numRead;
			}

			return convertArrayType(bytes);

		} catch (FileNotFoundException e) {
			System.err.println("File not found.");
		} catch (IOException e) {
			System.err.println("Cannot read from file.");
		}

		return null;
	}

	private static Byte[] convertArrayType(byte[] bytes) {

		Byte[] byteArray = new Byte[bytes.length];
		for (int i = 0; i < bytes.length; i++)
			byteArray[i] = Byte.valueOf(bytes[i]);

		return byteArray;
	}

	public static boolean validateAccount(String name, String pass) {
		boolean status = false;
		try {
			session = SessionManager.openSession();
		Query query = session.createQuery("from User u where u.userName=" + "'"
				+ name + "'" + " and u.userPass=" + "'" + pass + "'");

		List<User> users = query.list();

		if (!users.isEmpty() && users.get(0) != null) {
			if (users.get(0).getUserName().equals(name)) {
				status = true;
			}
		}
		} finally {
			if (session != null) {
				SessionManager.closeSession();
			}
		}
		return status;
	}

	public static boolean registerAccount(User user) {
		return addUser(user);
	}

	public static byte[] getThumbnailForPicture(String pathToPicture) {
		Thumbnail t = new Thumbnail();
		t.generateThumbnail(pathToPicture);
		return t.getThumbStream();
	}

	public static Session getSession() {
		return SessionManager.openSession();
	}

	public static boolean userExists(String userName) {
		boolean status = false;
		try {
			session = SessionManager.openSession();
		Query query = session.createQuery("from User u where u.userName='" + userName
				+ "'");
		if (query.list().isEmpty())
			status = false;
		status = true;
		} finally {
			if (session != null) {
				SessionManager.closeSession();
			}
		}
		return status;
	}

	public static boolean sendMail(String picId, String userId,
			String recepientEmail, String subject, String msgText) {

		Properties props = new Properties();
		props.put("mail.smtp.host", SMTP_SERVER);
		javax.mail.Session session = javax.mail.Session.getDefaultInstance(
				props, null);
		MimeMessage message = new MimeMessage(session);
		Address addressFrom = null;
		Address addressTo = null;
		try {
			User sender = getUserById(userId);
			Picture picture = getPicture(picId);
			addressFrom = new InternetAddress(sender.getEmail(), sender
					.getFName()
					+ " " + sender.getLName());
			addressTo = new InternetAddress(recepientEmail);
			message.setFrom(addressFrom);
			message.addRecipient(Message.RecipientType.TO, addressTo);
			message.setSubject(subject);
			Multipart multipart = new MimeMultipart();
			BodyPart messageBodyPart = new MimeBodyPart();
			messageBodyPart.setText(msgText);
			multipart.addBodyPart(messageBodyPart);
			messageBodyPart = new MimeBodyPart();
			DataSource source = new FileDataSource(picture.getPicturePath());
			messageBodyPart.setDataHandler(new DataHandler(source));
			messageBodyPart.setFileName(picture.getPicturePath());
			multipart.addBodyPart(messageBodyPart);
			message.setContent(multipart);
			Transport.send(message);
			return true;
		} catch (Exception e) {
			return false;
		}
	}
}
